#include "stdafx.h"
#include "Mmsystem.h"
#include "settings.h"
#include "global.h"
#include "ModelessMessageBox.h"
#include "microsipDlg.h"
#include "utf.h"
#include <io.h>
#include <afxinet.h>
#include <ws2tcpip.h>

#include <Strsafe.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


extern CmicrosipDlg *microsipDlg;

static void on_reg_state2(pjsua_acc_id acc_id,  pjsua_reg_info *info)
{
	if (IsWindow(microsipDlg->m_hWnd))
	{
#ifdef _GLOBAL_BALANCE
		pjsip_generic_string_hdr *hsr = NULL;
		const pj_str_t name = {"X-DSSBalance",12};
		hsr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name ( info->cbparam->rdata->msg_info.msg, &name, NULL);
		if (hsr) {
			microsipDlg->balance = PjToStr(&hsr->hvalue);
		} else {
			microsipDlg->balance = "";
		}
#endif

#if defined _GLOBAL_BALANCE || defined _GLOBAL_BALANCE_GTN || defined _GLOBAL_BALANCE_PLAIN || defined _GLOBAL_BALANCE_OPTIONS
		microsipDlg->SetTimer(IDT_TIMER_BALANCE,1000,NULL);
#endif
		microsipDlg->UpdateWindowText();
	}
}

static CString gethostbyaddrThreadResult;
static DWORD WINAPI gethostbyaddrThread( LPVOID lpParam ) 
{
	CString *addr = (CString *)lpParam;
	struct hostent *he = NULL;
	struct in_addr inaddr;
	inaddr.S_un.S_addr = inet_addr(*addr);;
	if (inaddr.S_un.S_addr != INADDR_NONE && inaddr.S_un.S_addr != INADDR_ANY) {
		he = gethostbyaddr((char *) &inaddr, 4, AF_INET);
		if (he) {
			gethostbyaddrThreadResult = he->h_name;
		}
	}
	//struct in_addr ipv4addr;
	//inet_pton(AF_INET, *addr, &ipv4addr);
	//he = gethostbyaddr((const char *)&ipv4addr, sizeof ipv4addr, AF_INET);
	//if (he) {
	//	gethostbyaddrThreadResult = he->h_name;
	//}
	delete addr;
	return 0;
}

static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
{
	if (!IsWindow(microsipDlg->m_hWnd))	{
		return;
	}
	pjsua_call_info call_info_t;
	if (pjsua_call_get_info(call_id, &call_info_t) != PJ_SUCCESS || call_info_t.state == PJSIP_INV_STATE_NULL) {
		return;
	}
	pjsua_call_info *call_info = &call_info_t;

	BOOL notShowWindow = FALSE;
	CString str;
	CString adder;
	CString number = PjToStr(&call_info->remote_info, TRUE);
	SIPURI sipuri;
	ParseSIPURI(number,&sipuri);

	if (call_info->state!=PJSIP_INV_STATE_DISCONNECTED && call_info->state!=PJSIP_INV_STATE_CONNECTING && call_info->remote_contact.slen>0) {
		SIPURI contactURI;
		ParseSIPURI(PjToStr(&call_info->remote_contact,TRUE),&contactURI);
		CString contactDomain = RemovePort(contactURI.domain);
		struct hostent *he = NULL;
		if (IsIP(contactDomain)) {
			HANDLE hThread;
			CString *addr = new CString;
			*addr = contactDomain;
			hThread = CreateThread(NULL,0, gethostbyaddrThread, addr, 0, NULL);
			if (WaitForSingleObject(hThread, 500)==0) {
				contactDomain = gethostbyaddrThreadResult;
			}
		}
		adder.AppendFormat("%s, ",contactDomain);
	}

	switch (call_info->state)
	{
	case PJSIP_INV_STATE_CALLING:
		str.Format("%s...",Translate("Calling"));
		break;
	case PJSIP_INV_STATE_INCOMING:
		notShowWindow = TRUE;
		str = Translate( "Incoming call" );
		break;
	case PJSIP_INV_STATE_EARLY:
		notShowWindow = TRUE;
		str = Translate( PjToStr(&call_info->last_status_text).GetBuffer() );
		break;
	case PJSIP_INV_STATE_CONNECTING:
		str.Format("%s...",Translate("Connecting"));
		break;
	case PJSIP_INV_STATE_CONFIRMED:
		if (! microsipDlg->callTimer) {
			microsipDlg->OnTimerCall(true);
			microsipDlg->callTimer = microsipDlg->SetTimer(IDT_TIMER_CALL,1000,NULL);
		}
		str = Translate("Connected");
		for (unsigned i=0;i<call_info->media_cnt;i++) {
			if (call_info->media[i].type == PJMEDIA_TYPE_AUDIO || call_info->media[i].type == PJMEDIA_TYPE_VIDEO) {
				pjsua_stream_info psi;
				if (pjsua_call_get_stream_info(call_info->id, call_info->media[i].index, &psi) == PJ_SUCCESS) {
					if (call_info->media[i].type == PJMEDIA_TYPE_AUDIO) {
						adder.AppendFormat("%s@%dkHz %dkbit/s, ",PjToStr (&psi.info.aud.fmt.encoding_name),psi.info.aud.fmt.clock_rate/1000, psi.info.aud.param->info.avg_bps/1000);
						if (psi.info.aud.proto==PJMEDIA_TP_PROTO_RTP_SAVP) {
							adder.AppendFormat("SRTP, ");
						}
					} else {
						adder.AppendFormat("%s %dkbit/s, ",
							PjToStr (&psi.info.vid.codec_info.encoding_name),
							psi.info.vid.codec_param->enc_fmt.det.vid.max_bps/1000
							);
					}
				}
			}
		}
		break;

	case PJSIP_INV_STATE_DISCONNECTED:
		call_deinit_tonegen(call_info->id);
		notShowWindow = TRUE;
		if (call_info->last_status == 200)
		{
			str = Translate("Call ended");
		} else {
			str = PjToStr(&call_info->last_status_text).GetBuffer();
			if (str == "Decline")
			{
				str = "Declined";
			}
			str=Translate(str.GetBuffer());
			if (accountSettings.singleMode && call_info->last_status != 487 &&  (call_info->last_status != 603 || call_info->role == PJSIP_ROLE_UAC))
			{
				microsipDlg->BaloonPopup(str, str, NIIF_INFO);
			}
		}
		break;
	}

	if (!accountSettings.singleMode) {
		if (call_info->state!=PJSIP_INV_STATE_CONFIRMED) {
			if (call_info->state!=PJSIP_INV_STATE_DISCONNECTED) {
				microsipDlg->UpdateWindowText(str, IDI_ONLINE);
			} else {
				microsipDlg->UpdateWindowText("-");
			}
		}
	}

	if (call_info->role==PJSIP_ROLE_UAC)
	{
		if (call_info->last_status == 180 && accountSettings.localDTMF)
		{
			microsipDlg->OnTimer(IDT_TIMER_1);
			microsipDlg->SetTimer(IDT_TIMER_1,4500,NULL);
		} else
		{
			if (microsipDlg->KillTimer(IDT_TIMER_1))
			{
				microsipDlg->RingIn();
			}
		}
	}
	MessagesContact* messagesContact = microsipDlg->messagesDlg->AddTab(number, "",
		(!accountSettings.singleMode &&
				(call_info->state == PJSIP_INV_STATE_CONFIRMED
				|| call_info->state == PJSIP_INV_STATE_CONNECTING)
		)
		||
		(accountSettings.singleMode
		&&
		(
		(call_info->role==PJSIP_ROLE_UAC && call_info->state != PJSIP_INV_STATE_DISCONNECTED)
		||
				(call_info->role==PJSIP_ROLE_UAS &&
				(call_info->state == PJSIP_INV_STATE_CONFIRMED
				|| call_info->state == PJSIP_INV_STATE_CONNECTING)
				)
		))
		?TRUE:FALSE
		, call_info, accountSettings.singleMode || notShowWindow, call_info->state == PJSIP_INV_STATE_DISCONNECTED);
	if (messagesContact) {
		if ( call_info->state == PJSIP_INV_STATE_DISCONNECTED) {
			messagesContact->mediaStatus = PJSUA_CALL_MEDIA_ERROR;
		}
		if (call_info->role==PJSIP_ROLE_UAS) {
			if ( call_info->state == PJSIP_INV_STATE_CONFIRMED) {
				microsipDlg->pageCalls->Add(call_info->call_id, messagesContact->number, messagesContact->name, MSIP_CALL_IN);
			} else if ( call_info->state == PJSIP_INV_STATE_INCOMING || call_info->state == PJSIP_INV_STATE_EARLY) {
				microsipDlg->pageCalls->Add(call_info->call_id, messagesContact->number, messagesContact->name , MSIP_CALL_MISS);
			}
		} else {
			if ( call_info->state == PJSIP_INV_STATE_CALLING) {
				microsipDlg->pageCalls->Add(call_info->call_id, messagesContact->number, messagesContact->name, MSIP_CALL_OUT);
			}
		}
	}
	if ( call_info->state == PJSIP_INV_STATE_DISCONNECTED) {
		microsipDlg->pageCalls->SetDuration(call_info->call_id, call_info->connect_duration.sec);
		if (call_info->last_status != 200) {
			microsipDlg->pageCalls->SetInfo(call_info->call_id, str);
		}
	}

	if (call_info->role==PJSIP_ROLE_UAS && call_info->state == PJSIP_INV_STATE_CONFIRMED)	{
		if (accountSettings.cmdCallAnswer !="" ) {
			ShellExecute(NULL, NULL, accountSettings.cmdCallAnswer, sipuri.user, NULL, SW_HIDE);
		}
	}

	if (accountSettings.singleMode)
	{
		if (call_info->state == PJSIP_INV_STATE_DISCONNECTED)
		{
			MessagesContact *messagesContactSelected = microsipDlg->messagesDlg->GetMessageContact();
			if (!messagesContactSelected || messagesContactSelected->callId==call_info->id || messagesContactSelected->callId==-1) {
				microsipDlg->pageDialer->SetNumber("");
				microsipDlg->pageDialer->UpdateCallButton(FALSE, 0);
			}
			microsipDlg->UpdateWindowText("-");
		} else {
			if (call_info->state!=PJSIP_INV_STATE_CONFIRMED) {
				microsipDlg->UpdateWindowText(str, IDI_ONLINE);
			}
			CTabCtrl* tab = (CTabCtrl*) microsipDlg->GetDlgItem(ID_TAB);
#ifndef _GLOBAL_PAGE_BUTTONS
			int tabN = 0;
#else
			int tabN = call_info->state == PJSIP_INV_STATE_CONFIRMED ? 2 : 0;
#endif
			if (tab->GetCurSel()!=tabN)
			{
				LRESULT pResult;
				microsipDlg->OnTcnSelchangingTab(NULL, &pResult);
				tab->SetCurSel(tabN);
				microsipDlg->OnTcnSelchangeTab(NULL, &pResult);
			}
			microsipDlg->messagesDlg->OnChangeTab(call_info);
		}
	} else {
		if (call_info->state == PJSIP_INV_STATE_DISCONNECTED) {
			microsipDlg->pageDialer->SetNumber("");
		}
	}

	if (messagesContact && str!="")	{
		if (adder!="") {
			str.AppendFormat(" (%s)", adder.Left(adder.GetLength()-2) );
		}
		microsipDlg->messagesDlg->AddMessage(messagesContact, str, MSIP_MESSAGE_TYPE_SYSTEM,
			call_info->state == PJSIP_INV_STATE_INCOMING || call_info->state == PJSIP_INV_STATE_EARLY
			);
	}

	if (call_info->state == PJSIP_INV_STATE_DISCONNECTED)
	{
		microsipDlg->messagesDlg->OnEndCall(call_info);
#ifdef _GLOBAL_VIDEO
		if (microsipDlg->previewWin && pjsua_call_get_count()<2)
		{
			microsipDlg->previewWin->PostMessage(WM_CLOSE, NULL, NULL);
		}
#endif
	}

#if defined _GLOBAL_BALANCE || defined _GLOBAL_BALANCE_GTN || defined _GLOBAL_BALANCE_PLAIN || defined _GLOBAL_BALANCE_OPTIONS
	if (call_info->state == PJSIP_INV_STATE_DISCONNECTED && pjsua_call_get_count()<2)
	{
		microsipDlg->SetTimer(IDT_TIMER_BALANCE,2000,NULL);
	}
#endif
	if (call_info->role==PJSIP_ROLE_UAS)
	{ 
		if (call_info->state == PJSIP_INV_STATE_DISCONNECTED || call_info->state == PJSIP_INV_STATE_CONFIRMED)
		{
			int count = microsipDlg->ringinDlgs.GetCount();
			for (int i = 0; i < count; i++ )
			{
				RinginDlg *ringinDlg = microsipDlg->ringinDlgs.GetAt(i);
				if ( call_info->id == ringinDlg->call_id)
				{
					if (count==1) {
						microsipDlg->RingIn();
					}
					microsipDlg->ringinDlgs.RemoveAt(i);
					microsipDlg->PostMessage(MYWM_DESTROY_RINGING, (WPARAM)ringinDlg);
					break;
				}
			}
		}
	}
}

static void on_call_media_state(pjsua_call_id call_id)
{
	pjsua_call_info call_info;
	if (pjsua_call_get_info(call_id, &call_info) != PJ_SUCCESS || call_info.state == PJSIP_INV_STATE_NULL) {
		return;
	}

	microsipDlg->SendMessage(MYWM_ON_CALL_MEDIA_STATE, (WPARAM) &call_info, 0);

	if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE
		|| call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD
		)
	{
		pjsua_conf_connect(call_info.conf_slot, 0);
		pjsua_conf_connect(0, call_info.conf_slot);
	} else {
		call_deinit_tonegen(call_id);
		pjsua_conf_disconnect(call_info.conf_slot, 0);
		pjsua_conf_disconnect(0, call_info.conf_slot);
	}
}

static void on_incoming_call(pjsua_acc_id acc, pjsua_call_id call_id,
							 pjsip_rx_data *rdata)
{
	pjsua_call_info call_info;
	pjsua_call_get_info(call_id,&call_info);
	SIPURI sipuri;
	ParseSIPURI(PjToStr(&call_info.remote_info), &sipuri);

	pjsua_call_id call_ids[PJSUA_MAX_CALLS];
	unsigned count = PJSUA_MAX_CALLS;
	if (pjsua_enum_calls ( call_ids, &count)==PJ_SUCCESS)  {
		for (unsigned i = 0; i < count; ++i) {
			pjsua_call_info call_info_curr;
			pjsua_call_get_info(call_ids[i], &call_info_curr);
			SIPURI sipuri_curr;
			ParseSIPURI(PjToStr(&call_info_curr.remote_info), &sipuri_curr);
			if (call_info_curr.id != call_info.id && 
				sipuri.user+"@"+sipuri.domain == sipuri_curr.user+"@"+sipuri_curr.domain
				) {
				pjsua_call_hangup(call_info.id, 486, NULL, NULL);
				return;
			}
		}
	}

	if (IsWindow(microsipDlg->m_hWnd))
	{
		if (microsipDlg->callIdImcomingIgnore!="" && microsipDlg->callIdImcomingIgnore == PjToStr(&call_info.call_id)) {
			microsipDlg->PostMessage(MYWM_CALL_ANSWER, (WPARAM)call_id, -487);
			return;
		}
		if (accountSettings.denyIncoming!="") {
			bool reject = false;
			if (accountSettings.denyIncoming=="user") {
				SIPURI sipuri;
				ParseSIPURI(PjToStr(&call_info.local_info), &sipuri);
				if (sipuri.user!=accountSettings.username) {
					reject = true;
				}
			} else if (accountSettings.denyIncoming=="domain") {
				SIPURI sipuri;
				ParseSIPURI(PjToStr(&call_info.local_info), &sipuri);
				if (accountSettings.domain!="") {
					if (sipuri.domain!=accountSettings.domain) {
						reject = true;
					}
				}
			} else if (accountSettings.denyIncoming=="rdomain") {
				if (accountSettings.domain!="") {
					if (sipuri.domain!=accountSettings.domain) {
						reject = true;
					}
				}
			} else if (accountSettings.denyIncoming=="address") {
				SIPURI sipuri;
				ParseSIPURI(PjToStr(&call_info.local_info), &sipuri);
				if (sipuri.user!=accountSettings.username || (accountSettings.domain != "" && sipuri.domain!=accountSettings.domain)) {
					reject = true;
				}
			} else if (accountSettings.denyIncoming=="all") {
				reject = true;
			}
			if (reject) {
				microsipDlg->PostMessage(MYWM_CALL_HANGUP, (WPARAM)call_id, NULL);
				return;
			}
		}

		if (accountSettings.cmdIncomingCall !="" ) {
			microsipDlg->SendMessage(MYWM_SHELL_EXECUTE, (WPARAM)accountSettings.cmdIncomingCall.GetBuffer(), (LPARAM)sipuri.user.GetBuffer());
		}

		int autoAnswer = accountSettings.autoAnswer;
		if (autoAnswer==2) {
			pjsip_generic_string_hdr *hsr = NULL;
			const pj_str_t header = pj_str("X-AUTOANSWER");
			hsr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name ( rdata->msg_info.msg, &header, NULL);
			if (hsr) {
				CString autoAnswerValue = PjToStr(&hsr->hvalue, TRUE);
				autoAnswerValue.MakeLower();
				if (autoAnswerValue == "true" || autoAnswerValue == "1") {
					autoAnswer = 1;
				}
			}
		}
		if (autoAnswer==2) {
			pjsip_generic_string_hdr *hsr = NULL;
			const pj_str_t header = pj_str("Call-Info");
			hsr = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name ( rdata->msg_info.msg, &header, NULL);
			if (hsr) {
				CString autoAnswerValue = PjToStr(&hsr->hvalue, TRUE);
				autoAnswerValue.MakeLower();
				if (autoAnswerValue == "auto answer" || autoAnswerValue == "answer-after=0") {
					autoAnswer = 1;
				}
			}
		}
		BOOL playBeep = FALSE;
		if (autoAnswer!=1 || pjsua_call_get_count()>1)
		{
			if (accountSettings.hidden) {
				microsipDlg->PostMessage(MYWM_CALL_HANGUP, (WPARAM)call_id, NULL);
			} else {
				microsipDlg->SendMessage(MYWM_CREATE_RINGING, (WPARAM)&call_info, (LPARAM) rdata);
				microsipDlg->PostMessage(MYWM_CALL_ANSWER, (WPARAM)call_id, -180);
				if (call_get_count_noincoming()) {
					playBeep = TRUE;
				} else {
					if (!accountSettings.ringingSound.GetLength()) {
						microsipDlg->RingIn("ringin.wav");
					} else
					{
						microsipDlg->RingIn(accountSettings.ringingSound);
					}
				}
			}
		} else
		{
			microsipDlg->PostMessage(MYWM_CALL_ANSWER, (WPARAM)call_id, (LPARAM)call_info.rem_vid_cnt);
			if (!accountSettings.hidden) {
				playBeep = TRUE;
			}
		}
		if (playBeep) {
			microsipDlg->RingIn("ringin2.wav", TRUE, TRUE);
		}
	}
}

static void on_nat_detect(const pj_stun_nat_detect_result *res)
{
	if (res->status != PJ_SUCCESS)
	{
		pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
	} else
	{
		if (res->nat_type == PJ_STUN_NAT_TYPE_SYMMETRIC)
		{
			if (IsWindow(microsipDlg->m_hWnd))
			{
				CString message = "STUN technology not works with Symmetric NAT. To avoid media problems, remove STUN server from account config.";
#ifndef _GLOBAL_CUSTOM
				message.Append(" For more info visit MicroSIP website, help page.");
#endif
				microsipDlg->BaloonPopup("Symmetric NAT detected!", message);
			}
		}
		PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
	}
}

#ifndef _GLOBAL_NO_CONTACTS
static void on_buddy_state(pjsua_buddy_id buddy_id)
{
	if (IsWindow(microsipDlg->m_hWnd)) {
		pjsua_buddy_info buddy_info;
		if (pjsua_buddy_get_info (buddy_id, &buddy_info) == PJ_SUCCESS) {
			Contact *contact = (Contact *) pjsua_buddy_get_user_data(buddy_id);
			int image;
			bool ringing = false;
			switch (buddy_info.status)
			{
			case PJSUA_BUDDY_STATUS_OFFLINE:
				image=1;
				break;
			case PJSUA_BUDDY_STATUS_ONLINE:
				if (PjToStr(&buddy_info.status_text)=="Ringing") {
					ringing = true;
				}
				if (PjToStr(&buddy_info.status_text)=="On the phone") {
					image=4;
				} else if (buddy_info.rpid.activity == PJRPID_ACTIVITY_AWAY)
				{
					image=2;
				} else if (buddy_info.rpid.activity == PJRPID_ACTIVITY_BUSY)
				{
					image=6;

				} else 
				{
					image=3;
				}
				break;
			default:
				image=0;
				break;
			}
			contact->image = image;
			contact->ringing = ringing;
			microsipDlg->PostMessage(MYWM_ON_BUDDY_STATE,(WPARAM)contact);
		}
	}
}
#endif

#ifndef _GLOBAL_NO_MESSAGING
static void on_pager(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, const pj_str_t *mime_type, const pj_str_t *body)
{
	if (IsWindow(microsipDlg->m_hWnd))
	{
		microsipDlg->SendMessage(MYWM_ON_PAGER, (WPARAM)from, (LPARAM)body);
	}
}

static void on_pager_status(pjsua_call_id call_id, const pj_str_t *to, const pj_str_t *body, void *user_data, pjsip_status_code status, const pj_str_t *reason)
{
	if (status != 200)
	{
		if (IsWindow(microsipDlg->m_hWnd))
		{
			microsipDlg->SendMessage(MYWM_ON_PAGER_STATUS, (WPARAM)to, (LPARAM)reason);
		}
	}
}
#endif

static void on_call_transfer_status(pjsua_call_id call_id,
									int status_code,
									const pj_str_t *status_text,
									pj_bool_t final,
									pj_bool_t *p_cont)
{
	pjsua_call_info call_info;
	MessagesContact* messagesContact = NULL;
	CString str;
	if (pjsua_call_get_info(call_id, &call_info) == PJ_SUCCESS) {
		CString number = PjToStr(&call_info.remote_info, TRUE);
		messagesContact = microsipDlg->messagesDlg->AddTab(number, "", FALSE, &call_info, TRUE, TRUE);
		if (messagesContact) {
			str.Format("%s: %.*s",
				Translate("Transfer status"),
				(int)status_text->slen, status_text->ptr);
			if (final) {
				str.AppendFormat(" [%s]", Translate("final"));
			}
			microsipDlg->messagesDlg->AddMessage(messagesContact, str);
		}
	}
	if (status_code/100 == 2) {
		if (messagesContact) {
			str = Translate("Call transfered successfully, disconnecting call");
			microsipDlg->messagesDlg->AddMessage(messagesContact, str);
		}
		microsipDlg->PostMessage(MYWM_CALL_HANGUP, (WPARAM)call_id, NULL);
		*p_cont = PJ_FALSE;
	}
}

static void on_call_media_event(pjsua_call_id call_id, unsigned med_idx, pjmedia_event *event)
{
	/*
	const pj_str_t codec_id = {"H264", 4};
	pjmedia_vid_codec_param param;
	pjsua_vid_codec_get_param(&codec_id, &param);

	param.dec_fmt.det.vid.size.w = 848;
	param.dec_fmt.det.vid.size.h = 480;

	param.dec_fmt.det.vid.fps.num = 30;
	param.dec_fmt.det.vid.fps.denum = 1;

	pjsua_vid_codec_set_param(&codec_id, &param);

	pjsua_call_set_vid_strm 	( 	call_id, PJSUA_CALL_VID_STRM_ADD, NULL ) 	;
	*/
}

static void on_call_tsx_state(pjsua_call_id call_id, pjsip_transaction *transaction, pjsip_event *event)
{
	/*
		pjsua_call_get_info(call_id, &call_info);
		CString s;
		s.Format("%d %s %s",call_info.state,PjToStr(&call_info.state_text),PjToStr(&call_info.state_text));
		AfxMessageBox(s);
		AfxMessageBox(PjToStr(&transaction->status_text));
	*/
}

void CmicrosipDlg::PJCreate()
{
#ifdef _GLOBAL_API
	CString data;

#ifndef _GLOBAL_API_SSL
#ifdef _GLOBAL_API_SSL2
	accountSettings.username = "";
	accountSettings.password = "";
	if (accountSettings.apiLogin!="" && accountSettings.apiPassword!="") {
		CInternetSession session;
		try {
			Json::Value root;
			CHttpConnection* m_pHttp = session.GetHttpConnection(_GLOBAL_API, 443, accountSettings.apiLogin, accountSettings.apiPassword);
			CHttpFile* pFile = m_pHttp->OpenRequest(CHttpConnection::HTTP_VERB_GET,_GLOBAL_API_PATH, 0, 1, 0, 0, INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_SECURE );
			pFile->SendRequest();
			DWORD dwStatusCode;
			pFile->QueryInfoStatusCode(dwStatusCode);
			if (dwStatusCode==200) {
				pFile->ReadString(data);
				if (data!="") {
					data = Utf8DecodeCP(data.GetBuffer(), CP_ACP, NULL);
					Json::Reader reader;
					bool parsedSuccess = reader.parse((LPCTSTR)data, 
						root, 
						false);
					if(parsedSuccess) {
						try {
							if (root["result_code"].asInt()==100) {
								accountSettings.server = root["data"]["server"].asString().c_str();
								accountSettings.proxy = root["data"]["proxy"].asString().c_str();
								accountSettings.domain = root["data"]["domain"].asString().c_str();
								accountSettings.username = root["data"]["username"].asString().c_str();
								accountSettings.authID = root["data"]["authid"].asString().c_str();
								accountSettings.password = root["data"]["password"].asString().c_str();
								accountSettings.displayName = root["data"]["displayname"].asString().c_str();
							} else if (root["result_message"].asString().c_str() != "") {
								if (root["result_code"].asInt()==24) {
									dwStatusCode = 401;
								} else {
									AfxMessageBox(root["result_message"].asString().c_str(), MB_ICONEXCLAMATION);
								}
							} else {
								AfxMessageBox("Wrong response", MB_ICONEXCLAMATION);
							}
						} catch (std::exception const& e) {}
					}
				}
			} else if (dwStatusCode == 401) {
				AfxMessageBox(Translate("Wrong credentials"), MB_ICONEXCLAMATION);
				onTrayNotify(NULL,WM_LBUTTONUP);
				OnMenuAccount();
			}
			pFile->Close();
			m_pHttp->Close();
			session.Close();
		} catch (CInternetException *e) {
			AfxMessageBox("Connection failed", MB_ICONEXCLAMATION);
		}
	}
#else
	if (accountSettings.apiLogin!="" && accountSettings.apiPassword!="") {
		CInternetSession session;
		try {
			CHttpConnection* m_pHttp = session.GetHttpConnection(_GLOBAL_API);
			CHttpFile* pFile = m_pHttp->OpenRequest("POST",_GLOBAL_API_PATH);
			CString strHeaders="Content-Type: application/x-www-form-urlencoded";
			CString strFormData;
			strFormData.Format("login=%s&password=%s",
				accountSettings.apiLogin,
				accountSettings.apiPassword
				);
			pFile->SendRequest(strHeaders,(LPVOID)strFormData.GetBuffer(),strFormData.GetLength());
			pFile->ReadString(data);
			pFile->Close();
			m_pHttp->Close();
			session.Close();
			if (data=="") {
				AfxMessageBox("Empty server response!", MB_ICONEXCLAMATION);
			} else if (data=="authentication failed") {
				AfxMessageBox("Authentication failed!", MB_ICONEXCLAMATION);
			} else {
				data = Utf8DecodeCP(data.GetBuffer(), CP_ACP, NULL);
			}
		} catch (CInternetException *e) {
			AfxMessageBox("Connection failed", MB_ICONEXCLAMATION);
		}
	}
	APIReadString("Server",&accountSettings.server,&data);
	APIReadString("Proxy",&accountSettings.proxy,&data);
	APIReadString("Domain",&accountSettings.domain,&data);
	APIReadString("Username",&accountSettings.username,&data);
	APIReadString("Password",&accountSettings.password,&data);
	APIReadString("DisplayName",&accountSettings.displayName,&data);
#endif
#else
	accountSettings.username = "";
	accountSettings.password = "";
	if (accountSettings.apiLogin!="" && accountSettings.apiPassword!="") {
		CInternetSession session;
		try {
			CHttpConnection* m_pHttp = session.GetHttpConnection(_GLOBAL_API, 443, _GLOBAL_API_LOGIN, _GLOBAL_API_PASSWORD);
			CHttpFile* pFile = m_pHttp->OpenRequest("POST",_GLOBAL_API_PATH, 0, 1, 0, 0, INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_SECURE);
			CString strHeaders="Content-Type: application/x-www-form-urlencoded";
			CString strFormData;
			Json::Value root;
			root["method"] = "GetUserCredentials";
			root["params"]["email"] = (LPCSTR) accountSettings.apiLogin;
			root["params"]["password"] = (LPCSTR) accountSettings.apiPassword;
			Json::FastWriter writer;
			std::string output = writer.write(root);
			strFormData.Format("request=%s",
				urlencode(output.c_str())
				);

			pFile->SendRequest(strHeaders,(LPVOID)strFormData.GetBuffer(),strFormData.GetLength());
			pFile->ReadString(data);
			pFile->Close();
			m_pHttp->Close();
			session.Close();
			if (data=="") {
				AfxMessageBox("Empty server response!", MB_ICONEXCLAMATION);
			} else if (data=="authentication failed") {
				AfxMessageBox("Authentication failed!", MB_ICONEXCLAMATION);
			} else {
				data = Utf8DecodeCP(data.GetBuffer(), CP_ACP, NULL);
				Json::Reader reader;
				bool parsedSuccess = reader.parse((LPCTSTR)data, 
					root, 
					false);
				if(parsedSuccess) {
					try {
						if (root["result_code"].asInt()==100) {
							accountSettings.username = root["data"]["device_extension"].asString().c_str();
							accountSettings.password = root["data"]["device_password"].asString().c_str();
						} else if (root["result_message"].asString().c_str() != "") {
							AfxMessageBox(root["result_message"].asString().c_str(), MB_ICONEXCLAMATION);
							if (root["result_code"].asInt()==24) {
								onTrayNotify(NULL,WM_LBUTTONUP);
								OnMenuAccount();
							}
						} else {
							AfxMessageBox("Connection failed", MB_ICONEXCLAMATION);
						}
					} catch (std::exception const& e) {}
				}
			}
		} catch (CInternetException *e) {
			AfxMessageBox("Connection failed", MB_ICONEXCLAMATION);
		}
	}
#endif

#endif

#ifndef _GLOBAL_NO_CONTACTS
	pageContacts->isSubscribed = FALSE;
#endif
	player_id = PJSUA_INVALID_ID;


	// check updates
#if !defined _GLOBAL_CUSTOM || defined _GLOBAL_UPDATES
	if (accountSettings.updatesInterval != "never") 
	{
		CTime t = CTime::GetCurrentTime();
		time_t time = t.GetTime();
		int days;
		if (accountSettings.updatesInterval =="daily")
		{
			days = 1;
		} else if (accountSettings.updatesInterval =="monthly")
		{
			days = 30;
		} else if (accountSettings.updatesInterval =="quarterly")
		{
			days = 90;
		} else
		{
			days = 7;
		}
		if (accountSettings.checkUpdatesTime + days * 86400 < time)
		{
			CheckUpdates();
			accountSettings.checkUpdatesTime = (int)time;
			accountSettings.Save();
		}
	}
#endif

	UsersDirectoryLoad();

	// pj create
	pj_status_t status;
	pjsua_config         ua_cfg;
	pjsua_media_config   media_cfg;
	pjsua_transport_config cfg;

	// Must create pjsua before anything else!
	status = pjsua_create();
	if (status != PJ_SUCCESS)
	{
		return;
	}

	// Initialize configs with default settings.
	pjsua_config_default(&ua_cfg);
	pjsua_media_config_default(&media_cfg);

	CString userAgent;
	if (accountSettings.userAgent=="") {
		userAgent.Format("%s/%s", _GLOBAL_NAME_NICE, _GLOBAL_VERSION);
		ua_cfg.user_agent = pj_str( userAgent.GetBuffer() );
	} else {
		ua_cfg.user_agent = pj_str( accountSettings.userAgent.GetBuffer() );
	}

	ua_cfg.cb.on_reg_state2=&on_reg_state2;
	ua_cfg.cb.on_call_state=&on_call_state;
	
	
	
	ua_cfg.cb.on_call_tsx_state=&on_call_tsx_state;
	

	ua_cfg.cb.on_call_media_state = &on_call_media_state;
	ua_cfg.cb.on_incoming_call = &on_incoming_call;
	ua_cfg.cb.on_nat_detect= &on_nat_detect;
#ifndef _GLOBAL_NO_CONTACTS
	ua_cfg.cb.on_buddy_state = &on_buddy_state;
#endif
#ifndef _GLOBAL_NO_MESSAGING
	ua_cfg.cb.on_pager = &on_pager;
	ua_cfg.cb.on_pager_status = &on_pager_status;
#endif
	ua_cfg.cb.on_call_transfer_status = &on_call_transfer_status;
	ua_cfg.cb.on_call_media_event = &on_call_media_event;

	ua_cfg.srtp_secure_signaling=0;

	if (accountSettings.srtp =="optional")
	{
		ua_cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
	} else if (accountSettings.srtp =="mandatory")
	{
		ua_cfg.use_srtp = PJMEDIA_SRTP_MANDATORY;
	} else
	{
		ua_cfg.use_srtp = PJMEDIA_SRTP_DISABLED;
	}

	if (accountSettings.stun.GetLength()) 
	{
		ua_cfg.stun_srv_cnt=1;
		ua_cfg.stun_srv[0] = pj_str( accountSettings.stun.GetBuffer() );
	}

	media_cfg.enable_ice = accountSettings.ice ? PJ_TRUE : PJ_FALSE;
	media_cfg.no_vad = accountSettings.disableVAD ? PJ_TRUE : PJ_FALSE;
	
	media_cfg.clock_rate=44100;
	media_cfg.channel_count=2;

#ifdef _GLOBAL_PTIME
	media_cfg.ptime = _GLOBAL_PTIME;
#endif
#ifdef _GLOBAL_JB_MAX
	media_cfg.jb_max=_GLOBAL_JB_MAX;
#endif
#ifdef _GLOBAL_JB_MAX_PRE
	media_cfg.jb_max_pre=_GLOBAL_JB_MAX_PRE;
#endif
#ifdef _GLOBAL_JB_INIT
	media_cfg.jb_init=_GLOBAL_JB_INIT;
#endif


	// Initialize pjsua
#ifndef _GLOBAL_NO_LOG
	if (accountSettings.enableLog) {
		pjsua_logging_config log_cfg;
		pjsua_logging_config_default(&log_cfg);
		log_cfg.log_filename = pj_str(accountSettings.logFile.GetBuffer());
		status = pjsua_init(&ua_cfg, &log_cfg, &media_cfg);
	} else {
		status = pjsua_init(&ua_cfg, NULL, &media_cfg);
	}
#else
	status = pjsua_init(&ua_cfg, NULL, &media_cfg);
#endif

	if (status != PJ_SUCCESS)
	{
		pjsua_destroy();
		return;
	}

	// Start pjsua
	status = pjsua_start();
	if (status != PJ_SUCCESS)
	{
		pjsua_destroy();
		return;
	}

	// Set snd devices
	audio_input=-1;
	audio_output=-2;
	audio_ring=-2;

	unsigned count = 64;
	pjmedia_aud_dev_info aud_dev_info[64];
	pjsua_enum_aud_devs(aud_dev_info, &count);
	for (unsigned i=0;i<count;i++)
	{
		if (aud_dev_info[i].input_count && !accountSettings.audioInputDevice.Compare(aud_dev_info[i].name)) {
			audio_input = i;
		}
		if (aud_dev_info[i].output_count) {
			if (!accountSettings.audioOutputDevice.Compare(aud_dev_info[i].name)) {
				audio_output = i;
			}
			if (!accountSettings.audioRingDevice.Compare(aud_dev_info[i].name)) {
				audio_ring = i;
			}
		}
	}
	//Set aud codecs prio
#ifdef _GLOBAL_CODECS_ENABLED
	if (accountSettings.audioCodecs=="")
	{
		accountSettings.audioCodecs = _GLOBAL_CODECS_ENABLED;
	}
#endif
	if (accountSettings.audioCodecs.GetLength())
	{
		pjsua_codec_info codec_info[64];
		unsigned count = 64;
		pjsua_enum_codecs(codec_info, &count);
		for (unsigned i=0;i<count;i++)
		{
			pjsua_codec_set_priority(&codec_info[i].codec_id,0);
		}
		CString resToken;
		int curPos = 0;
		int i = 255;
		resToken= accountSettings.audioCodecs.Tokenize(" ",curPos);
		while (resToken.GetLength())
		{
			pj_str_t codec_id = pj_str(StrToPj(resToken));
			pjsua_codec_set_priority(&codec_id, i);
			resToken= accountSettings.audioCodecs.Tokenize(" ",curPos);
			i--;
		}
	}
#ifdef _GLOBAL_VIDEO
	//Set vid codecs prio
	if (accountSettings.videoCodec.GetLength())
	{
		pj_str_t codec_id = pj_str(StrToPj(accountSettings.videoCodec));
		pjsua_vid_codec_set_priority(&codec_id,255);
	}
	int bitrate;
	if (accountSettings.disableH264) {
		pjsua_vid_codec_set_priority(&pj_str("H264"),0);
	} else
	{
		const pj_str_t codec_id = {"H264", 4};
		pjmedia_vid_codec_param param;
		pjsua_vid_codec_get_param(&codec_id, &param);
		param.enc_fmt.det.vid.size.w = 640;
		param.enc_fmt.det.vid.size.h = 480;
		param.enc_fmt.det.vid.fps.num = 30;
		param.enc_fmt.det.vid.fps.denum = 1;
#ifdef _GLOBAL_H264_BITRATE
		if (accountSettings.bitrateH264=="") {
			accountSettings.bitrateH264 = _GLOBAL_H264_BITRATE;
		}
#endif
		if (atoi(accountSettings.bitrateH264)) {
			bitrate = 1000 * atoi(accountSettings.bitrateH264);
			param.enc_fmt.det.vid.avg_bps = bitrate;
			param.enc_fmt.det.vid.max_bps = bitrate;
		}
		param.dec_fmt.det.vid.size.w = 640;
		param.dec_fmt.det.vid.size.h = 480;

		param.dec_fmt.det.vid.fps.num = 30;
		param.dec_fmt.det.vid.fps.denum = 1;

		/*
		// Defaut (level 1e, 30):
		param.dec_fmtp.cnt = 2;
		param.dec_fmtp.param[0].name = pj_str("profile-level-id");
		param.dec_fmtp.param[0].val = pj_str("42e01e");
		param.dec_fmtp.param[1].name = pj_str("packetization-mode");
		param.dec_fmtp.param[1].val = pj_str("1");
		//*/

		pjsua_vid_codec_set_param(&codec_id, &param);
	}
	if (accountSettings.disableH263) {
		pjsua_vid_codec_set_priority(&pj_str("H263"),0);
	} else {
#ifdef _GLOBAL_H263_BITRATE
		if (accountSettings.bitrateH263=="") {
			accountSettings.bitrateH263 = _GLOBAL_H263_BITRATE;
		}
#endif
		if (atoi(accountSettings.bitrateH263)) {
			const pj_str_t codec_id = {"H263", 4};
			pjmedia_vid_codec_param param;
			pjsua_vid_codec_get_param(&codec_id, &param);
			bitrate = 1000 * atoi(accountSettings.bitrateH263);
			param.enc_fmt.det.vid.avg_bps = bitrate;
			param.enc_fmt.det.vid.max_bps = bitrate;
			pjsua_vid_codec_set_param(&codec_id, &param);
		}
	}
#endif

	// Create transport
	transport_udp = -1;
	transport_tcp = -1;
	transport_tls = -1;

	pjsua_transport_config_default(&cfg);
	cfg.public_addr = pj_str( accountSettings.publicAddr.GetBuffer() );

#ifdef _GLOBAL_DSCP_CONTROL
	cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP;
	cfg.qos_params.dscp_val = _GLOBAL_DSCP_CONTROL;
#endif

	int listenPort = accountSettings.listenPort!="" ? atoi(accountSettings.listenPort) : 5060;

	cfg.port = listenPort;
	status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, &transport_udp);
	if (status != PJ_SUCCESS)
	{
		cfg.port=0;
		pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, &transport_udp);
	}
#ifdef _GLOBAL_ACCOUNT_TRANSPORT
	if (_GLOBAL_ACCOUNT_TRANSPORT != "udp") {
#endif
	cfg.port = listenPort;
	status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &cfg, &transport_tcp);
	if (status != PJ_SUCCESS)
	{
		cfg.port=0;
		pjsua_transport_create(PJSIP_TRANSPORT_TCP, &cfg, &transport_tcp);
	}
	cfg.port = listenPort + 1;
	status = pjsua_transport_create(PJSIP_TRANSPORT_TLS, &cfg, &transport_tls);
	if (status != PJ_SUCCESS)
	{
		cfg.port=0;
		pjsua_transport_create(PJSIP_TRANSPORT_TLS, &cfg, &transport_tls);
	}
#ifdef _GLOBAL_ACCOUNT_TRANSPORT
	}
#endif
}

void CmicrosipDlg::PJDestroy()
{
#ifndef _GLOBAL_NO_CONTACTS
	if (pageContacts) {
		pageContacts->PresenceUnsubsribe();
	}
#endif
	call_deinit_tonegen(-1);
	pjsua_destroy();
}




